using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Configuration;
using System.IO;
using System.Web;
using System.Web.Hosting;
using System.Web.Profile;
using Jaws.Core.Web.Entities;
using Jaws.Core.Web.Entities.Profiles;

namespace Jaws.Core.Web.Providers
{
    /// <summary>
    /// 
    /// </summary>
    public class XmlProfileProvider : ProfileProvider
    {
        ProfileCollection profiles = XmlCollectionBase<ProfileCollection, Profile>.Load();

        private string _applicationName;

        private string DataStoreFile
        {
            get
            {
                var path = SiteSettings.Instance().DataStoragePath();
                return Path.Combine(path, string.Format("Profiles.xml"));
            }
        }

        /// <summary>
        /// Gets or sets the name of the currently running application.
        /// </summary>
        /// <value></value>
        /// <returns>A <see cref="T:System.String"></see> that contains the application's shortened name, which does not contain a full path or extension, for example, SimpleAppSettings.</returns>
        public override string ApplicationName
        {
            get { return _applicationName; }
            set { _applicationName = value; }
        }

        /// <summary>
        /// Initializes the provider.
        /// </summary>
        /// <param name="name">The friendly name of the provider.</param>
        /// <param name="config">A collection of the name/value pairs representing the provider-specific attributes specified in the configuration for this provider.</param>
        /// <exception cref="T:System.ArgumentNullException">The name of the provider is null.</exception>
        /// <exception cref="T:System.InvalidOperationException">An attempt is made to call <see cref="M:System.Configuration.Provider.ProviderBase.Initialize(System.String,System.Collections.Specialized.NameValueCollection)"></see> on a provider after the provider has already been initialized.</exception>
        /// <exception cref="T:System.ArgumentException">The name of the provider has a length of zero.</exception>
        public override void Initialize(string name, NameValueCollection config)
        {
            if (config == null)
            {
                throw new ArgumentNullException("config");
            }
            if (String.IsNullOrEmpty(name))
            {
                name = "XmlProfileProvider";
            }

            base.Initialize(name, config);

            if (!String.IsNullOrEmpty(config["applicationName"]))
            {
                _applicationName = config["applicationName"];
            }
        }

        /// <summary>
        /// When overridden in a derived class, deletes all user-profile data for profiles in which the last activity date occurred before the specified date.
        /// </summary>
        /// <param name="authenticationOption">One of the <see cref="T:System.Web.Profile.ProfileAuthenticationOption"></see> values, specifying whether anonymous, authenticated, or both types of profiles are deleted.</param>
        /// <param name="userInactiveSinceDate">A <see cref="T:System.DateTime"></see> that identifies which user profiles are considered inactive. If the <see cref="P:System.Web.Profile.ProfileInfo.LastActivityDate"></see>  value of a user profile occurs on or before this date and time, the profile is considered inactive.</param>
        /// <returns>
        /// The number of profiles deleted from the data source.
        /// </returns>
        public override int DeleteInactiveProfiles(ProfileAuthenticationOption authenticationOption,
                                                   DateTime userInactiveSinceDate)
        {
            
            var toBeRemoved = new List<Profile>();

            foreach (Profile p in profiles)
            {
                switch (authenticationOption)
                {
                    case ProfileAuthenticationOption.All:
                        if (p.LastActivityDate <= userInactiveSinceDate)
                        {
                            toBeRemoved.Add(p);
                        }
                        break;
                    case ProfileAuthenticationOption.Anonymous:
                        if (p.LastActivityDate <= userInactiveSinceDate && p.IsAnonymous)
                        {
                            toBeRemoved.Add(p);
                        }
                        break;
                    case ProfileAuthenticationOption.Authenticated:
                        if (p.LastActivityDate <= userInactiveSinceDate && !p.IsAnonymous)
                        {
                            toBeRemoved.Add(p);
                        }
                        break;
                }
            }

            for (int i = 0; i < toBeRemoved.Count; i++)
            {
                profiles.Remove(toBeRemoved[i]);
            }

            return toBeRemoved.Count;
        }

        /// <summary>
        /// When overridden in a derived class, deletes profile properties and information for profiles that match the supplied list of user names.
        /// </summary>
        /// <param name="usernames">A string array of user names for profiles to be deleted.</param>
        /// <returns>
        /// The number of profiles deleted from the data source.
        /// </returns>
        public override int DeleteProfiles(string[] usernames)
        {
            int counter = 0;
            ProfileCollection profiles = ProfileCollection.Load(DataStoreFile);

            foreach (string name in usernames)
            {
                Collection<Profile> p = profiles.FindByUserName(name, ProfileAuthenticationOption.All);
                if (p.Count > 0)
                {
                    for (int i = 0; i < p.Count; i++)
                    {
                        profiles.Remove(p[i]);
                        counter++;
                    }
                }
            }

            profiles.Save(DataStoreFile);

            return counter;
        }

        /// <summary>
        /// When overridden in a derived class, deletes profile properties and information for the supplied list of profiles.
        /// </summary>
        /// <param name="profiles">A <see cref="T:System.Web.Profile.ProfileInfoCollection"></see>  of information about profiles that are to be deleted.</param>
        /// <returns>
        /// The number of profiles deleted from the data source.
        /// </returns>
        public override int DeleteProfiles(ProfileInfoCollection profiles)
        {
            var usernames = new List<string>();
            foreach (ProfileInfo pi in profiles)
            {
                usernames.Add(pi.UserName);
            }

            return DeleteProfiles(usernames.ToArray());
        }

        /// <summary>
        /// When overridden in a derived class, retrieves profile information for profiles in which the last activity date occurred on or before the specified date and the user name matches the specified user name.
        /// </summary>
        /// <param name="authenticationOption">One of the <see cref="T:System.Web.Profile.ProfileAuthenticationOption"></see> values, specifying whether anonymous, authenticated, or both types of profiles are returned.</param>
        /// <param name="usernameToMatch">The user name to search for.</param>
        /// <param name="userInactiveSinceDate">A <see cref="T:System.DateTime"></see> that identifies which user profiles are considered inactive. If the <see cref="P:System.Web.Profile.ProfileInfo.LastActivityDate"></see> value of a user profile occurs on or before this date and time, the profile is considered inactive.</param>
        /// <param name="pageIndex">The index of the page of results to return.</param>
        /// <param name="pageSize">The size of the page of results to return.</param>
        /// <param name="totalRecords">When this method returns, contains the total number of profiles.</param>
        /// <returns>
        /// A <see cref="T:System.Web.Profile.ProfileInfoCollection"></see> containing user profile information for inactive profiles where the user name matches the supplied usernameToMatch parameter.
        /// </returns>
        public override ProfileInfoCollection FindInactiveProfilesByUserName(
            ProfileAuthenticationOption authenticationOption, string usernameToMatch, DateTime userInactiveSinceDate,
            int pageIndex, int pageSize, out int totalRecords)
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// When overridden in a derived class, retrieves profile information for profiles in which the user name matches the specified user names.
        /// </summary>
        /// <param name="authenticationOption">One of the <see cref="T:System.Web.Profile.ProfileAuthenticationOption"></see> values, specifying whether anonymous, authenticated, or both types of profiles are returned.</param>
        /// <param name="usernameToMatch">The user name to search for.</param>
        /// <param name="pageIndex">The index of the page of results to return.</param>
        /// <param name="pageSize">The size of the page of results to return.</param>
        /// <param name="totalRecords">When this method returns, contains the total number of profiles.</param>
        /// <returns>
        /// A <see cref="T:System.Web.Profile.ProfileInfoCollection"></see> containing user-profile information for profiles where the user name matches the supplied usernameToMatch parameter.
        /// </returns>
        public override ProfileInfoCollection FindProfilesByUserName(ProfileAuthenticationOption authenticationOption,
                                                                     string usernameToMatch, int pageIndex, int pageSize,
                                                                     out int totalRecords)
        {
            ProfileCollection profiles = ProfileCollection.Load(DataStoreFile);
            ProfileInfoCollection pic = profiles.CreateProfileInfoCollection(authenticationOption);
            var lst = new List<ProfileInfo>();
            foreach (ProfileInfo p in pic)
            {
                if (p.UserName.Contains(usernameToMatch))
                {
                    lst.Add(p);
                }
            }

            totalRecords = lst.Count;

            var output = new ProfileInfoCollection();
            for (int i = (pageIndex*pageSize); i < ((pageIndex*pageSize) + pageSize); i++)
            {
                if (lst.Count < i)
                {
                    break;
                }
                output.Add(lst[i]);
            }

            return output;
        }

        /// <summary>
        /// When overridden in a derived class, retrieves user-profile data from the data source for profiles in which the last activity date occurred on or before the specified date.
        /// </summary>
        /// <param name="authenticationOption">One of the <see cref="T:System.Web.Profile.ProfileAuthenticationOption"></see> values, specifying whether anonymous, authenticated, or both types of profiles are returned.</param>
        /// <param name="userInactiveSinceDate">A <see cref="T:System.DateTime"></see> that identifies which user profiles are considered inactive. If the <see cref="P:System.Web.Profile.ProfileInfo.LastActivityDate"></see>  of a user profile occurs on or before this date and time, the profile is considered inactive.</param>
        /// <param name="pageIndex">The index of the page of results to return.</param>
        /// <param name="pageSize">The size of the page of results to return.</param>
        /// <param name="totalRecords">When this method returns, contains the total number of profiles.</param>
        /// <returns>
        /// A <see cref="T:System.Web.Profile.ProfileInfoCollection"></see> containing user-profile information about the inactive profiles.
        /// </returns>
        public override ProfileInfoCollection GetAllInactiveProfiles(ProfileAuthenticationOption authenticationOption,
                                                                     DateTime userInactiveSinceDate, int pageIndex,
                                                                     int pageSize, out int totalRecords)
        {
            ProfileCollection profiles = ProfileCollection.Load(DataStoreFile);

            ProfileInfoCollection pic = profiles.CreateProfileInfoCollection(authenticationOption);
            var lst = new List<ProfileInfo>();

            foreach (ProfileInfo pi in pic)
            {
                if (pi.LastActivityDate <= userInactiveSinceDate)
                {
                    lst.Add(pi);
                }
            }

            totalRecords = lst.Count;
            int counter = 0;

            var newPic = new ProfileInfoCollection();
            foreach (ProfileInfo pi in lst)
            {
                if (counter > (pageIndex*pageSize) && counter < ((pageIndex*pageSize) + pageSize))
                {
                    newPic.Add(pi);
                }
                counter++;
            }

            return newPic;
        }

        /// <summary>
        /// When overridden in a derived class, retrieves user profile data for all profiles in the data source.
        /// </summary>
        /// <param name="authenticationOption">One of the <see cref="T:System.Web.Profile.ProfileAuthenticationOption"></see> values, specifying whether anonymous, authenticated, or both types of profiles are returned.</param>
        /// <param name="pageIndex">The index of the page of results to return.</param>
        /// <param name="pageSize">The size of the page of results to return.</param>
        /// <param name="totalRecords">When this method returns, contains the total number of profiles.</param>
        /// <returns>
        /// A <see cref="T:System.Web.Profile.ProfileInfoCollection"></see> containing user-profile information for all profiles in the data source.
        /// </returns>
        public override ProfileInfoCollection GetAllProfiles(ProfileAuthenticationOption authenticationOption,
                                                             int pageIndex, int pageSize, out int totalRecords)
        {
            ProfileCollection profiles = ProfileCollection.Load(DataStoreFile);

            ProfileInfoCollection pic = profiles.CreateProfileInfoCollection(authenticationOption);
            var newPic = new ProfileInfoCollection();

            totalRecords = pic.Count;
            int counter = 0;

            foreach (ProfileInfo pi in pic)
            {
                if (counter > (pageIndex*pageSize) && counter < ((pageIndex*pageSize) + pageSize))
                {
                    newPic.Add(pi);
                }
                counter++;
            }

            return newPic;
        }

        /// <summary>
        /// When overridden in a derived class, returns the number of profiles in which the last activity date occurred on or before the specified date.
        /// </summary>
        /// <param name="authenticationOption">One of the <see cref="T:System.Web.Profile.ProfileAuthenticationOption"></see> values, specifying whether anonymous, authenticated, or both types of profiles are returned.</param>
        /// <param name="userInactiveSinceDate">A <see cref="T:System.DateTime"></see> that identifies which user profiles are considered inactive. If the <see cref="P:System.Web.Profile.ProfileInfo.LastActivityDate"></see>  of a user profile occurs on or before this date and time, the profile is considered inactive.</param>
        /// <returns>
        /// The number of profiles in which the last activity date occurred on or before the specified date.
        /// </returns>
        public override int GetNumberOfInactiveProfiles(ProfileAuthenticationOption authenticationOption,
                                                        DateTime userInactiveSinceDate)
        {
            ProfileCollection profiles = ProfileCollection.Load(DataStoreFile);
            ProfileInfoCollection pic = profiles.CreateProfileInfoCollection(authenticationOption);

            int counter = 0;
            foreach (ProfileInfo p in pic)
            {
                if (p.LastActivityDate <= userInactiveSinceDate)
                {
                    counter++;
                }
            }

            return counter;
        }

        /// <summary>
        /// Returns the collection of settings property values for the specified application instance and settings property group.
        /// </summary>
        /// <param name="context">A <see cref="T:System.Configuration.SettingsContext"></see> describing the current application use.</param>
        /// <param name="collection">A <see cref="T:System.Configuration.SettingsPropertyCollection"></see> containing the settings property group whose values are to be retrieved.</param>
        /// <returns>
        /// A <see cref="T:System.Configuration.SettingsPropertyValueCollection"></see> containing the values for the specified settings property group.
        /// </returns>
        public override SettingsPropertyValueCollection GetPropertyValues(SettingsContext context,
                                                                          SettingsPropertyCollection collection)
        {
            ProfileCollection profiles = ProfileCollection.Load(DataStoreFile);
            Collection<Profile> profile = null;

            string userID = context["UserName"].ToString();
            bool authenticated = Convert.ToBoolean(context["IsAuthenticated"]);

            if (authenticated)
            {
                profile = profiles.FindByUserName(userID, ProfileAuthenticationOption.Authenticated);
            }
            else
            {
                profile = profiles.FindByUserName(userID, ProfileAuthenticationOption.Anonymous);
            }


            var spvc = new SettingsPropertyValueCollection();
            Profile prof = null;

            if (profile.Count > 0)
            {
                prof = profile[0];
            }


            foreach (SettingsProperty prop in collection)
            {
                var val = new SettingsPropertyValue(prop);
                if (prof != null && prof.Properties.ContainsKey(prop.Name))
                {
                    if (val.Deserialized)
                    {
                        val.PropertyValue = prof.Properties[prop.Name].Value;
                    }
                    else
                    {
                        val.SerializedValue = prof.Properties[prop.Name].Value;
                        if (prop.SerializeAs == SettingsSerializeAs.ProviderSpecific)
                        {
                            val.PropertyValue = Convert.ChangeType(val.SerializedValue, prop.PropertyType);
                        }
                    }
                    val.IsDirty = false;
                }
                spvc.Add(val);
            }

            return spvc;
        }

        /// <summary>
        /// Sets the values of the specified group of property settings.
        /// </summary>
        /// <param name="context">A <see cref="T:System.Configuration.SettingsContext"></see> describing the current application usage.</param>
        /// <param name="collection">A <see cref="T:System.Configuration.SettingsPropertyValueCollection"></see> representing the group of property settings to set.</param>
        public override void SetPropertyValues(SettingsContext context, SettingsPropertyValueCollection collection)
        {
            ProfileCollection profiles = ProfileCollection.Load(DataStoreFile);
            Collection<Profile> profile = null;

            string userID = context["UserName"].ToString();
            bool authenticated = Convert.ToBoolean(context["IsAuthenticated"]);

            if (authenticated)
            {
                profile = profiles.FindByUserName(userID, ProfileAuthenticationOption.Authenticated);
            }
            else
            {
                profile = profiles.FindByUserName(userID, ProfileAuthenticationOption.Anonymous);
            }

            Profile p = null;

            if (profile.Count > 0)
            {
                p = profile[0];
            }
            else
            {
                p = new Profile();
                p.IsAnonymous = !authenticated;
                p.UserName = userID;
                profiles.Add(p);
            }

            foreach (SettingsPropertyValue val in collection)
            {
                if (val.SerializedValue != null)
                {
                    if (p.Properties.ContainsKey(val.Name))
                    {
                        p.Properties[val.Name].Value = val.SerializedValue;
                    }
                    else
                    {
                        var pro = new ProfileProperty(val.Name);
                        pro.Value = val.SerializedValue;
                        p.Properties.Add(pro);
                    }
                }
            }

            profiles.Save(DataStoreFile);
        }
    }
}